package com.louis.ylog.filter;

import com.alibaba.fastjson.JSON;
import com.dianping.cat.Cat;
import com.dianping.cat.configuration.NetworkInterfaceManager;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import com.louis.ylog.ELKTagBuilder;
import com.louis.ylog.TraceContext;
import com.louis.ylog.feign.CatConstantsExt;
import com.louis.ylog.feign.CatContextImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

/**
 * http协议传输，远程调用链目标端接收context的filter，
 * 通过header接收rootId、parentId、childId并放入CatContextImpl中，调用Cat.logRemoteCallServer()进行调用链关联
 */
@Slf4j
public class CatTransactionFilter implements Filter {



    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        boolean isAjaxRequest=!StringUtils.isBlank(servletRequest.getContentType())&&servletRequest.getContentType().toLowerCase().contains("json");

        //非ajax請求
        if(!isAjaxRequest){
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        //elk cat均未启用时禁用此filter逻辑
        if(!TraceContext.isCatEnable()&&!TraceContext.isElkEnable()){
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        BodyCachingHttpServletRequestWrapper requestWrapper =
                new BodyCachingHttpServletRequestWrapper((HttpServletRequest) servletRequest);
        BodyCachingHttpServletResponseWrapper responseWrapper =
                new BodyCachingHttpServletResponseWrapper((HttpServletResponse) servletResponse);

        String catTitle=String.format("%s[%s]",request.getRequestURI().toLowerCase(),request.getMethod());
        Transaction t = TraceContext.isCatEnable()? Cat.newTransaction(catTitle, NetworkInterfaceManager.INSTANCE.getLocalHostAddress()):null;
        TraceContext.reset();
        TraceContext.setRootTransType(catTitle);


        if(TraceContext.isCatEnable()){
            Cat.logMetricForCount("http-request-transaction");
        }
        //若header中有context相关属性，则生成调用链，若无，仅统计请求的Transaction
        if(TraceContext.isCatEnable() && null != request.getHeader(CatConstantsExt.CAT_HTTP_HEADER_ROOT_MESSAGE_ID)){
            CatContextImpl catContext = new CatContextImpl();
            catContext.addProperty(Cat.Context.ROOT,request.getHeader(CatConstantsExt.CAT_HTTP_HEADER_ROOT_MESSAGE_ID));
            catContext.addProperty(Cat.Context.PARENT,request.getHeader(CatConstantsExt.CAT_HTTP_HEADER_PARENT_MESSAGE_ID));
            catContext.addProperty(Cat.Context.CHILD,request.getHeader(CatConstantsExt.CAT_HTTP_HEADER_CHILD_MESSAGE_ID));
            Cat.logRemoteCallServer(catContext);

            //PigeonService 的 Cross报表
            if(!StringUtils.isBlank(request.getHeader(CatConstantsExt.CAT_HTTP_HEADER_Request_Domain))){
                createServerCross(request);
            }
        }

        log.info(ELKTagBuilder.create("通用接口日志").putCustomerField("CatTitle",catTitle).build(),"header:{}",requestWrapper.getHeaderStr());
        log.info(ELKTagBuilder.create("通用接口日志").putCustomerField("CatTitle",catTitle).build(),"request body: {}",requestWrapper.getBodyStr());

        try {
            filterChain.doFilter(requestWrapper, responseWrapper);
            log.info(ELKTagBuilder.create("通用接口日志").putCustomerField("CatTitle",catTitle).build(),"response: {}",responseWrapper.getBodyStr());
            //记录响应结果
            traceAPIResult(catTitle,responseWrapper.getBodyStr());
            if(TraceContext.isCatEnable()){
                Cat.logMetricForDuration("http-request-duration",t.getDurationInMillis());
            }
            if(null!=t){
                t.setStatus(Transaction.SUCCESS);
            }
        }catch (IOException e){
            if(null!=t){
                t.setStatus(e);
            }
            throw e;
        }catch (ServletException e){
            if(null!=t){
                t.setStatus(e);
            }
            throw e;
        }finally {
            if(null!=t){
                t.complete();
            }
        }
    }

    private void createServerCross(HttpServletRequest request){
        try {
            Transaction crossT=Cat.newTransaction(CatConstantsExt.CROSS_SERVER,request.getRequestURI());
            Cat.logEvent( CatConstantsExt.PROVIDER_CALL_APP,request.getHeader(CatConstantsExt.CAT_HTTP_HEADER_Request_Domain));
            Cat.logEvent(CatConstantsExt.PROVIDER_CALL_SERVER,request.getRemoteHost());
            crossT.setStatus(Transaction.SUCCESS);
            crossT.complete();
        }catch (Exception ex){
            log.error("创建服务端Cross异常",ex);
        }
    }


    //最好放到另外一个切面中
    private void traceAPIResult(String catTitle, String responseBody){
        if(!TraceContext.isCatEnable()){
            return;
        }
        try {
            BaseResponseBody body=null;
            try {
                if(!StringUtils.isBlank(responseBody)){
                    body= JSON.parseObject(responseBody,BaseResponseBody.class);
                }
            }catch (Throwable e){
                log.warn("非框架标准接口， CatTitle: {}",catTitle,e);
            }
            String resDesc="非框架标准接口, 无法解析code, message";
            boolean resSuccess=false;
            if(body!=null){
                resSuccess=(body.getResultCode()==0);
                resDesc=String.format("%s_%s_%s",body.getResultCode(),body.getResultMessage(),body.getDebugMessage());
            }
            String catName=catTitle+"_"+  resDesc;
            //全部结果
            Transaction t = Cat.newTransaction(CatConstantsExt.Type_Response_Result, URLEncoder.encode(catName,"utf-8"));
            if(resSuccess){
                t.setStatus(Message.SUCCESS);
            }else {
                t.setStatus(new Exception("非成功操作"));
            }
            t.complete();

            if(!resSuccess){
                //异常结果
                t = Cat.newTransaction(CatConstantsExt.Type_Response_Result_Exception, URLEncoder.encode(catName,"utf-8"));
                t.setStatus(new Exception(catName));
                t.complete();
            }
        }catch (Exception e){
            log.warn("cat result 异常",e);
        }

    }

    @Override
    public void destroy() {

    }
}
